Passed
Push — main ( d84d54...d45772 )
by Andrii
02:30
created

index.ts ➔ classNaming   A

Complexity

Conditions 1

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 14
dl 0
loc 26
rs 9.7
c 0
b 0
f 0
1
import type {
2
  CssModule,
3
  ClassHash,
4
  ClassNamed,
5
  Action,
6
  RequiredKeys
7
} from "./defs"
8
import {
9
  joinWithLead,
10
  resolver,
11
  wrapper
12
} from "./core"
13
import { emptize } from "./utils"
14
import { EMPTY_OBJECT, stackedKey } from "./consts"
15
16
emptize(classNaming)
17
emptize(classes)
18
19
export type { ClassNames, ClassHash, ClassNamesProperty, ClassNamed } from "./defs"
20
export default classNaming
21
export {classNamesCheck} from "./check"
22
23
/** Set context
24
 * @example
25
 * ```typescript
26
 *   const classes = classNaming(this.props)
27
 *   const classes = classNaming({classnames: require("./some.css"), className?})
28
 *   const classes = classNaming<Props>()
29
 *   const classes = classNaming<MyClassNames>()
30
 * ```
31
 */
32
function classNaming<
33
  Ctx extends {classnames: Source, className?: string},
34
  Source extends CssModule = Ctx["classnames"]
35
>(
36
  context: Ctx = EMPTY_OBJECT as Ctx
37
) {
38
  const {classnames, className = ""} = context
39
  classnames && emptize(classnames)
40
  
41
  const host: ClassNamingCall<Source, {}> = classes.bind({
42
    classnames,
43
    className,
44
    [stackedKey]: undefined
45
  })
46
47
  return wrapper(host, className)
48
}
49
50
/// CONTEXTED. TS-notation not matters
51
52
function classes<
53
  Source extends CssModule,
54
  Actions extends undefined | {[K in keyof Source]?: Action}
55
>(
56
  this: ClassNamingThis<Source>,
57
  arg0?: string | true | {[K in keyof Actions]: K extends keyof Source ? Action : never},
58
  arg1?: [Extract<typeof arg0, undefined|true|string>] extends [never] ? never : Actions
59
): ClassNaming<Source, {}> {
60
  const {
61
    className,
62
    classnames,
63
    [stackedKey]: preStacked,
64
  } = this
65
  , withPropagation = arg0 === true  
66
  , source = typeof arg0 === "object" ? arg0 as Actions: arg1 as Actions
67
  , allowed = source && resolver(classnames, source!)
68
  , withInjection = typeof arg0 !== "string" ? preStacked : joinWithLead(preStacked, arg0)
69
  , stacked = joinWithLead(withInjection, allowed)
70
  , result = joinWithLead(withPropagation && className, stacked)
71
  , host: ClassNamingCall<
72
    {[K in Exclude<keyof Source, keyof Actions>]: ClassHash},
73
    {}
74
  >
75
  = classes.bind({classnames, className, [stackedKey]: stacked})
76
77
  classnames && emptize(classnames)
78
79
  return wrapper(
80
    host,
81
    result,
82
  )
83
}   
84
85
// Making as interface breaks stuff
86
type ClassNamingCall<Source extends CssModule, Used extends CssModule> =
87
/** 
88
 * @example
89
 * ```typescript
90
 *   classes();
91
 *   classes(true); classes("App");
92
 *   classes({App}); classes({App: true, "App--bad": false});
93
 * 
94
 *   const btn = classes(className, {Btn})
95
 *   btn({Btn__disabled: true});
96
 * ```
97
 * @example
98
 * ```tsx
99
 *   <div {...classes(...)} />
100
 *   <div data-block={`${classes(...)}`} />
101
 *   <Component {...{
102
 *     ...classes(...)(...)(...)},
103
 *     ...classnames
104
 *   }/>
105
 * ```
106
 */
107
 <
108
  Actions0 extends {[K in keyof Source]?: Action},
109
  Actions1 extends {[K in keyof Source]?: Action}
110
 >(
111
    arg0?: true | string | Actions0
112
    & {[K in keyof Actions0]: K extends keyof Source ? K extends keyof Used ? never : Action : never},
113
    arg1?: {[K in keyof Source]?: Action} extends Actions0 ? Actions1
114
    & {[K in keyof Actions1]: K extends keyof Source ? K extends keyof Used ? never : Action : never} : never
115
  ) => ClassNaming<
116
    {[K in Exclude<keyof Source,
117
      RequiredKeys<Actions0> | RequiredKeys<Actions1>
118
    >]: ClassHash},
119
    {[K in keyof Used
120
      | RequiredKeys<Actions0> | RequiredKeys<Actions1>
121
    ]: ClassHash}
122
  >
123
;
124
125
//TODO #11 no `className` - no first `true`
126
type ClassNaming<Source extends CssModule, Used extends CssModule> = ClassNamed & ClassNamingCall<Source, Used>
127
128
type ClassNamingThis<Source extends CssModule> = ClassNamingContext<Source> & {
129
  [stackedKey]: string|undefined
130
}
131
132
type ClassNamingContext<T extends CssModule> = Partial<ClassNamed & {
133
  classnames: T
134
}>
135